package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.guigu.common.exception.BusinessException;
import com.atguigu.guigu.common.result.ResultCodeEnum;
import com.atguigu.guigu.common.utils.Assert;
import com.atguigu.service.base.utils.*;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.TransFlowService;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestHeader;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.sql.Wrapper;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 用户账户 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-11-19
 */
@Service
@Slf4j
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {
    @Resource
    UserInfoService userInfoService;
    @Resource
    TransFlowService transFlowService;
    @Resource
    RabbitTemplate rabbitTemplate;
//    @Resource
//    UserAccountService userAccountService;
    public void updateUserAccount(BigDecimal voteAmt, Long userId) {
        UserAccount userAccount = this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
        if (userAccount == null) {
            //新建账户
            userAccount = new UserAccount();
            userAccount.setUserId(userId);
            userAccount.setFreezeAmount(new BigDecimal("0"));
            userAccount.setAmount(voteAmt);
            this.save(userAccount);
        } else {
            //更新账户
            userAccount.setAmount(userAccount.getAmount().add(voteAmt));
            this.updateById(userAccount);
        }
    }

    @Override
    public String charge(String token, Double amount) {
        //1、验证登录状态
        Long userId = JwtUtils.getUserId(token);
        //2、验证充值用户的状态：必须正常、已绑定hfb
        UserInfo userInfo = userInfoService.getById(userId);
        Assert.isTrue(userInfo.getStatus()!=1 , ResultCodeEnum.LOGIN_LOCKED_ERROR);
        Assert.isTrue(userInfo.getBindStatus()!=1 , ResultCodeEnum.USER_NO_BIND_ERROR);
        //3、充值金额大于0
        Assert.isTrue(amount<=0 , ResultCodeEnum.USER_CHARGE_LESS_ERROR);
        //4、封装hfb需要的参数生成表单字符串
        Map<String, Object> map = new HashMap<>();
        map.put("agentId",HfbConst.AGENT_ID);//srb在hfb平台的唯一id
        String chargeNo = LendNoUtils.getChargeNo();
        map.put("agentBillNo",chargeNo );//本次充值的唯一编号(防止恶意攻击、在回调接口中操作同一个重新记录)
        map.put("bindCode",userInfo.getBindCode());//本次充值会员在hfb中的账户
        //文档中说明了只支持小数点后两位
        map.put("chargeAmt",new BigDecimal(amount).divide(new BigDecimal("1"),2,BigDecimal.ROUND_DOWN));
        map.put("feeAmt",new BigDecimal("0"));
        map.put("notifyUrl",HfbConst.RECHARGE_NOTIFY_URL);//异步回调的接口地址必须修改为虚拟机nginx的地址
        map.put("returnUrl",HfbConst.RECHARGE_RETURN_URL);
        map.put("timestamp",System.currentTimeMillis());
        String sign = RequestHelper.getSign(map);
        map.put("sign",sign);

        //用户账户初始化：  在回调接口中初始化
        //保存交易流水
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userId);
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(chargeNo);
        //交易类型（1：充值 2：提现 3：投标 4：投资回款 ...）
        transFlow.setTransType(1);
        transFlow.setTransTypeName("充值");
        transFlow.setTransAmount((BigDecimal) map.get("chargeAmt"));
        transFlow.setStatus(0);
        transFlowService.save(transFlow);
        //参数1：封装的自动提交的表单的action地址  ，参数2: 需要提交给汇付宝的参数集合
        return FormHelper.buildForm(HfbConst.RECHARGE_URL,map);
    }

    @Override
    public String chargeNotify(HttpServletRequest request) {
        //1、获取参数
        Map<String, String[]> map = request.getParameterMap();
        Map<String, Object> params = RequestHelper.switchMap(map);
        //2、验证签名
        boolean b = RequestHelper.isSignEquals(params);
//        Assert.notTrue(b , ResultCodeEnum.CALLBACK_PARAM_ERROR);
        if(!b){
            //签名验证失败
            log.error("充值回调签名验证失败：{}" , JSON.toJSONString(params) );
//            throw new BusinessException(ResultCodeEnum.CALLBACK_PARAM_ERROR);
            return "fail";
        }
        //3、验证hfb业务响应状态码
        String resultCode = params.get("resultCode").toString();
        String agentBillNo = params.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));

        if(transFlow==null){
            log.error("充值回调失败,非法回调：{}",  JSON.toJSONString(map));
            return "fail";
        }
        if(transFlow.getStatus()!=0){
            log.error("充值重复回调：{}",  JSON.toJSONString(map));
            return transFlow.getStatus()==1?"success":"fail";
        }
//        Assert.notTrue("0001".equals(resultCode) , ResultCodeEnum.CALLBACK_PARAM_ERROR);
        if(!"0001".equals(resultCode)){
            //hfb处理充值业务失败
            transFlow.setMemo(params.get("resultMsg").toString());
            transFlow.setStatus(-1);
            transFlowService.updateById(transFlow);
            log.error("充值回调业务状态码验证失败：{}" , JSON.toJSONString(params) );
//            throw new BusinessException(ResultCodeEnum.CALLBACK_PARAM_ERROR);
            return "fail";
        }
        //4、验证充值金额是否正确
        BigDecimal transAmount = transFlow.getTransAmount();
        BigDecimal chargeAmt = new BigDecimal(params.get("chargeAmt").toString());
        if(transAmount.doubleValue()!=chargeAmt.doubleValue()){
            //用户在汇付宝充值的金额和当前平台用户要充值的金额不一致
            transFlow.setMemo(params.get("resultMsg").toString());
            transFlow.setStatus(-1);
            transFlowService.updateById(transFlow);
            log.error("充值回调充值金额校验失败：{},预计充值金额:{},实际充值金额:{}" ,
                    JSON.toJSONString(params) ,transAmount,chargeAmt );
//            throw new BusinessException(ResultCodeEnum.USER_CHARGE_ERROR);
            return "fail";
        }


        //5、更新充值交易流水为已完成
        transFlow.setStatus(1);
        transFlow.setMemo(JSON.toJSONString(params));
        transFlowService.updateById(transFlow);
        //6、将充值信息保存到用户账户表中
        //根据回调传入的bindCode查询会员对象数据
        String bindCode = params.get("bindCode").toString();

        UserInfo userInfo = userInfoService.getOne(Wrappers.lambdaQuery(UserInfo.class)
                .eq(UserInfo::getBindCode, bindCode));
        UserAccount userAccount = this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userInfo.getId()));
        if(userAccount==null){
            //如果充值的用户还没有本地账户，新建账户初始化
            userAccount = new UserAccount();
            userAccount.setUserId(userInfo.getId());
            userAccount.setAmount(chargeAmt);
            //冻结金额：用户投资时 投资还未完成
            userAccount.setFreezeAmount(new BigDecimal("0"));
            this.save(userAccount);
        }else{
            //如果用户已存在账户，在他之前的账户上添加充值金额
            userAccount.setAmount(userAccount.getAmount().add(chargeAmt));
            this.updateById(userAccount);
        }

        //充值成功：给充值用户发送短信通知
        // 向mq中发送一条消息，表示充值成功  并传入参数(充值用户手机号..操作类型)
        Map<String,String> message = new HashMap<>();
        message.put("mobile", userInfo.getMobile());
        message.put("type","1");//充值=1
        message.put("amount",transAmount.toString());
        message.put("token", UUID.randomUUID().toString().replace("-",""));
        rabbitTemplate.convertAndSend("exchange.srb","srb.account.sms",
                message);

        //远程调用发送短信：阻塞执行
        return "success";
    }
    //获取投资回报
    @Override
    public BigDecimal getInterestCount(BigDecimal invest, Integer period,
                                       BigDecimal yearRate, Integer returnMethod) {
        BigDecimal interest;
        //根据不同的还款方式调用不同的工具类计算回报
        switch (returnMethod){
            case 1://等额本息
                interest = Amount1Helper.getInterestCount(invest,yearRate,period);
                break;
            case 2://等额本金
                interest = Amount2Helper.getInterestCount(invest,yearRate,period);
                break;
            case 3://每月还息一次还本
                interest = Amount3Helper.getInterestCount(invest,yearRate,period);
                break;
            case 4://一次还本还息
                interest = Amount4Helper.getInterestCount(invest,yearRate,period);
                break;
            default:
                interest = new BigDecimal("0");
                break;
        }
        return interest;
    }

    @Override
    public String withDraw(BigDecimal money, String token) {
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = userInfoService.checkStatus(userId);
        Assert.notTrue(money.doubleValue()>0 , ResultCodeEnum.USER_WITHDRAW_ERROR);
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        // 方式CSRF跨站点攻击   也表示唯一的一次操作
        String withdrawNo = LendNoUtils.getWithdrawNo().toString();
        params.put("agentBillNo",withdrawNo);
        params.put("bindCode",userInfo.getBindCode());
        params.put("fetchAmt",money);
        // 借款人还款时，投资人回款时 扣除投资人收益的手续费
        params.put("feeAmt",new BigDecimal("0"));
        params.put("returnUrl",HfbConst.WITHDRAW_RETURN_URL);
        params.put("notifyUrl",HfbConst.WITHDRAW_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());
        String sign = RequestHelper.getSign(params);
        params.put("sign",sign);
        //保存提现日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userId);
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(withdrawNo);
        //交易类型（1：充值 2：提现 3：投标 4：投资回款 ...）
        transFlow.setTransType(2);
        transFlow.setTransTypeName("提现");
        transFlow.setTransAmount(money);
        transFlow.setStatus(0);
        transFlowService.save(transFlow);


        return  FormHelper.buildForm(HfbConst.WITHDRAW_URL,params);
    }
    //回调
    @Override
    public String notifyWithdraw(HttpServletRequest request) {
        //1、判断回调参数签名是否正确
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = RequestHelper.switchMap(parameterMap);
        boolean b = RequestHelper.isSignEquals(map);
        if(!b){
            //签名校验失败
            log.error("提现回调失败,签名校验错误：{}",  JSON.toJSONString(map));
            return "fail";
        }
        //2、获取状态码是否正确
        String resultCode = map.get("resultCode").toString();
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        //如果transFlow为空 代表非法回调 、如果agentBillNo的状态不为0 代表hfb已经访问过该接口并且已经处理过业务
        if(transFlow==null){
            log.error("提现回调失败,非法回调：{}",  JSON.toJSONString(map));
            return "fail";
        }
        if(transFlow.getStatus()!=0){
            log.error("提现重复回调：{}",  JSON.toJSONString(map));
            return transFlow.getStatus()==1?"success":"fail";
        }
        transFlow.setMemo(JSON.toJSONString(map));
        if(!"0001".equals(resultCode)){
            log.error("提现回调失败,状态码错误：{}",  JSON.toJSONString(map));
            //更新提现操作状态为失败
            transFlow.setStatus(-1);
            transFlowService.updateById(transFlow);
            return "fail";
        }
        //3、判断用户提现的金额是否正确
        BigDecimal fetchAmt = new BigDecimal(map.get("fetchAmt").toString());
        //获取构建表单时提交的提现金额 和回调时的提现金额比较
        if(transFlow==null  || transFlow.getTransAmount().doubleValue()!=fetchAmt.doubleValue()){
            log.error("提现回调失败,提现金额错误：{}",  JSON.toJSONString(map));
            //更新提现操作状态为失败
            transFlow.setStatus(-1);
            transFlowService.updateById(transFlow);
            return "fail";
        }
        //4、更新用户账户余额
        UserAccount userAccount = this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, transFlow.getUserId()));
        userAccount.setAmount(userAccount.getAmount().subtract(fetchAmt));
        this.updateById(userAccount);

        transFlow.setStatus(1);
        transFlowService.updateById(transFlow);
        return "success";
    }

}
